/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.xpath.model;

import java.text.MessageFormat;
import java.util.ResourceBundle;
import javax.swing.tree.DefaultMutableTreeNode;
import offset.nodes.Constants;
import offset.nodes.client.model.SimpleNamespaceRegistry;

/**
 *
 * @author Walter Lütgenau
 */
public class XPathQueryWriter {

    String typeConstraint;
    String pathConstraint = "//";
    DefaultMutableTreeNode valueConstraint;
    String[] columnSpecifier;
    SortColumn[] orderingSpecifier;

    public void setColumnSpecifier(String[] columnSpecifier) {
        this.columnSpecifier = columnSpecifier;
    }

    public void setPathConstraint(String pathConstraint) {
        this.pathConstraint = pathConstraint;
    }

    public void setOrderingSpecifier(SortColumn[] orderingSpecifier) {
        this.orderingSpecifier = orderingSpecifier;
    }

    public void setTypeConstraint(String typeConstraint) {
        this.typeConstraint = typeConstraint;
    }

    public void setValueConstraint(DefaultMutableTreeNode valueConstraint) {
        this.valueConstraint = valueConstraint;
    }

    protected boolean empty(Object[] array) {
        if (array == null || array.length == 0)
            return true;
        return false;
    }

    class XPath {

        protected String valueConstraintToString(DefaultMutableTreeNode constraint) {
            if (constraint == null)
                return "";

            if (constraint.getUserObject() instanceof ExpressionNode)
                return constraint.getUserObject().toString();

            StringBuffer buf = new StringBuffer();
            String logicalOperator = null;

            if (constraint.getUserObject() instanceof AndNode)
                logicalOperator = " and ";
            else if (constraint.getUserObject() instanceof OrNode)
                logicalOperator = " or ";

            if (constraint.getChildCount() > 1)
                buf.append("(");

            for (int i = 0; i < constraint.getChildCount(); i++) {
                if (i > 0)
                    buf.append(logicalOperator);
                buf.append(valueConstraintToString((DefaultMutableTreeNode) constraint.getChildAt(i)));
            }

            if (constraint.getChildCount() > 1)
                buf.append(")");

            return buf.toString();
        }

        protected String columnSpecifierToString(String[] columnNames) {
            if (empty(columnNames))
                return "";

            StringBuffer result = new StringBuffer();

            result.append("/(");
            for (int i = 0; i < columnNames.length; i++) {
                if (i > 0)
                    result.append(" | ");
                result.append("@" + columnNames[i]);
            }
            result.append(")");

            return result.toString();
        }

        protected String orderingSpecifierToString(SortColumn[] sortColumns) {
            if (empty(sortColumns))
                return "";

            StringBuffer result = new StringBuffer();

            result.append(" order by ");
            for (int i = 0; i < sortColumns.length; i++) {
                if (i > 0)
                    result.append(", ");
                result.append("@" + sortColumns[i].getProperty() + " " + sortColumns[i].getDirection());
            }

            return result.toString();
        }

        public String toString() {
            StringBuffer result = new StringBuffer(pathConstraint);

            if (typeConstraint != null && typeConstraint.length() > 0)
                result.append("//element(*, " + typeConstraint + ")");
            if (valueConstraint != null && valueConstraint.getChildCount() > 0)
                result.append(" [" + valueConstraintToString(valueConstraint) + "]");
            result.append(columnSpecifierToString(columnSpecifier));
            result.append(orderingSpecifierToString(orderingSpecifier));

            return result.toString();
        }
    }

    class HumanDescription {

        ResourceBundle bundle = java.util.ResourceBundle.getBundle("offset/nodes/client/view/resources/ClientBundle"); // NOI18N

        protected String getPropertyName(String childName, String propertyName) {
            StringBuffer buf = new StringBuffer();
            if (childName != null && childName.length() > 0)
                buf.append(childName + Constants.PATH_SEPARATOR);
            buf.append(propertyName);

            return buf.toString();
        }

        protected String pathConstraintToString(String path) {
            if (path == null || path.length() == 0)
                return "";

            if (path.equals(Constants.PATH_SEPARATOR))
                return bundle.getString("xpathQuery.description.anyPath");

            if (path.startsWith(Constants.PATH_SEPARATOR))
                path = path.substring(1);

            String[] pathComponents = path.split("/");

            StringBuffer result = new StringBuffer();
            result.append(MessageFormat.format(bundle.getString("xpathQuery.description.firstPathComponent"), pathComponents[0]));
            for (int i = 1; i < pathComponents.length; i++) {
                result.append(MessageFormat.format(bundle.getString("xpathQuery.description.pathComponent"), pathComponents[i]));
            }
            result.append(".\n");

            return result.toString();
        }

        protected String typeConstraintToString(String type) {
            return MessageFormat.format(bundle.getString("xpathQuery.description.typeConstraint"), type);
        }

        protected String expressionToString(ExpressionNode expression) {
            XPathOperator xop = XPathOperators.get(expression.getOperator());

            if (xop != null)
                if (xop.getType() == XPathOperatorType.operator)
                    return getPropertyName(expression.getChildName(), expression.getPropertyName()) + " " + xop.getXPathOperator() + " '" + expression.getValue() + "'";
                else if (xop.getType() == XPathOperatorType.function)
                    return getPropertyName(expression.getChildName(), expression.getPropertyName()) + " "
                            + SimpleNamespaceRegistry.getInstance().toQName(xop.getXPathOperator()).getLocalName() + " '" + expression.getValue() + "'";

            return "";
        }

        protected String valueConstraintToString(DefaultMutableTreeNode constraint) {
            if (constraint == null)
                return "";

            if (constraint.getUserObject() instanceof ExpressionNode)
                return expressionToString((ExpressionNode) constraint.getUserObject());

            StringBuffer result = new StringBuffer();
            String logicalOperator = null;

            if (constraint.getUserObject() instanceof AndNode)
                logicalOperator = " and ";
            else if (constraint.getUserObject() instanceof OrNode)
                logicalOperator = " or ";

            if (constraint.getChildCount() > 1)
                result.append("(");

            for (int i = 0; i < constraint.getChildCount(); i++) {
                if (i > 0)
                    result.append(logicalOperator);
                result.append(valueConstraintToString((DefaultMutableTreeNode) constraint.getChildAt(i)));
            }

            if (constraint.getChildCount() > 1)
                result.append(")");

            return result.toString();
        }

        protected String columnSpecifierToString(String[] columnNames) {
            if (empty(columnNames))
                return "";

            StringBuffer result = new StringBuffer();

            result.append(" ");
            result.append(bundle.getString("xpathQuery.description.columnConstraint") + " ");
            for (int i = 0; i < columnNames.length; i++) {
                if (i > 0)
                    result.append(", ");
                result.append(columnNames[i]);
            }

            return result.toString();
        }

        protected String orderingSpecifierToString(SortColumn[] sortColumns) {
            if (empty(sortColumns))
                return "";

            StringBuffer result = new StringBuffer();

            result.append(bundle.getString("xpathQuery.description.orderBy") + " ");
            for (int i = 0; i < sortColumns.length; i++) {
                if (i > 0)
                    result.append(", ");
                result.append(sortColumns[i].getProperty() + " " + sortColumns[i].getDirection());
            }
            result.append(".");

            return result.toString();
        }

        public String toString() {
            StringBuffer result = new StringBuffer(pathConstraintToString(pathConstraint));

            if (typeConstraint != null && typeConstraint.length() > 0)
                result.append(typeConstraintToString(typeConstraint));
            if (valueConstraint.getChildCount() > 0) {
                result.append(" \n");
                result.append(bundle.getString("xpathQuery.description.valueConstraintStart"));
                result.append(" " + valueConstraintToString(valueConstraint));
                result.append(". ");
            }
            result.append(columnSpecifierToString(columnSpecifier));
            if (!empty(columnSpecifier) && empty(orderingSpecifier))
                result.append(".");
            result.append(orderingSpecifierToString(orderingSpecifier));

            return result.toString();
        }
    }

    public String getXPathQuery() {
        XPath xpath = new XPath();
        return xpath.toString();
    }

    public String getHumanDescription() {
        HumanDescription description = new HumanDescription();
        return description.toString();
    }
}
